import json
from PyQt6.QtCore import QObject, pyqtSignal
from PyQt6.QtNetwork import QHostAddress

class UdpCommunicationEngine(QObject):
    """
    UDP通信引擎 - 基于PyQt6 QUdpSocket的后端。
    """
    # 信号定义
    send_status = pyqtSignal(str, bool)  # (message, success)
    receive_status = pyqtSignal(str, str)  # (sender, message)
    error_occurred = pyqtSignal(str)
    
    def __init__(self):
        from PyQt6.QtNetwork import QUdpSocket
        super().__init__()
        self.udp_socket = QUdpSocket()
        self.local_port = None
        self.is_listening = False
        # 连接socket信号。
        self.udp_socket.readyRead.connect(self.on_ready_read)
        self.udp_socket.errorOccurred.connect(self.on_socket_error)
    
    def start_listening(self, port):
        """
        开始监听指定端口。
        """
        try:
            if self.is_listening:
                self.stop_listening()
            self.local_port = int(port)
            # 绑定到指定端口。
            if self.udp_socket.bind(QHostAddress.SpecialAddress.AnyIPv4, self.local_port):
                self.is_listening = True
                return True
            else:
                self.error_occurred.emit(f"无法绑定到端口 {self.local_port}")
                return False
                
        except Exception as e:
            self.error_occurred.emit(f"启动监听失败: {str(e)}")
            return False
    
    def stop_listening(self):
        """
        停止监听。
        """
        if self.is_listening:
            self.udp_socket.close()
            self.is_listening = False
    
    def send_json_message(self, target_ip, target_port, json_data):
        """
        发送JSON消息到指定目标。
        """
        try:
            port = int(target_port)  # 验证目标端口。
            if isinstance(json_data, str):  # 准备消息数据。
                try:
                    json.loads(json_data)  # 验证字符串是否为有效JSON。
                    message_data = json_data.encode('utf-8')
                except json.JSONDecodeError:
                    self.error_occurred.emit("JSON格式无效。")
                    return False
            else:
                message_data = json.dumps(json_data, ensure_ascii=False).encode('utf-8')
            target_address = QHostAddress(target_ip)  # 创建目标地址。
            bytes_written = self.udp_socket.writeDatagram(message_data, target_address, port)  # 发送数据报。
            
            if bytes_written > 0:
                self.send_status.emit(f"消息已发送到 {target_ip}:{port}。", True)
                return True
            else:
                error_msg = "发送失败：无法写入数据。"
                self.send_status.emit(error_msg, False)
                self.error_occurred.emit(error_msg)
                return False
            
        except Exception as e:
            error_msg = f"发送失败: {str(e)}。"
            self.send_status.emit(error_msg, False)
            self.error_occurred.emit(error_msg)
            return False
    
    def on_ready_read(self):
        """
        处理接收到的数据。
        """
        while self.udp_socket.hasPendingDatagrams():
            # 读取数据报。
            datagram = self.udp_socket.receiveDatagram()
            data = datagram.data().data()
            sender_host = datagram.senderAddress().toString()
            sender_port = datagram.senderPort()
            sender_info = f"{sender_host}:{sender_port}"
            try:
                message = data.decode('utf-8')  # 尝试解码为UTF-8文本。
                
                # 尝试解析JSON（用于验证，但不改变原消息）。
                try:
                    json.loads(message)
                    # 如果是有效JSON，按JSON处理。
                    formatted_message = message
                except json.JSONDecodeError:
                    # 如果不是JSON，保持原样。
                    formatted_message = message
                self.receive_status.emit(sender_info, formatted_message)
            except UnicodeDecodeError:
                # 如果不是文本，显示十六进制。
                hex_data = data.hex()
                self.receive_status.emit(sender_info, f"原始数据: {hex_data}")
    
    def on_socket_error(self, socket_error):
        """
        处理socket错误。
        """
        if self.is_listening:  # 只在监听状态下报告错误。
            error_msg = f"Socket错误: {self.udp_socket.errorString()}"
            self.error_occurred.emit(error_msg)
    
    def broadcast_message(self, port, json_data):
        """
        广播消息到指定端口。
        """
        try:
            if isinstance(json_data, str):
                message_data = json_data.encode('utf-8')
            else:
                message_data = json.dumps(json_data, ensure_ascii=False).encode('utf-8')
            # 发送广播。
            bytes_written = self.udp_socket.writeDatagram(
                message_data, 
                QHostAddress.SpecialAddress.Broadcast, 
                port
            )
            if bytes_written > 0:
                self.send_status.emit(f"广播消息已发送到端口 {port}", True)
                return True
            else:
                error_msg = "广播发送失败"
                self.send_status.emit(error_msg, False)
                return False
        except Exception as e:
            error_msg = f"广播发送失败: {str(e)}"
            self.send_status.emit(error_msg, False)
            self.error_occurred.emit(error_msg)
            return False

from PyQt6.QtWidgets import QMessageBox

class CommunicationWindow:
    def __init__(self):
        from PyQt6.uic.load_ui import loadUi
        from PyQt6.QtWidgets import (QMainWindow, QTextEdit, QLineEdit, 
                                    QPushButton, QLabel, QSpinBox)
        ui: QMainWindow = loadUi("./udp通信窗口.ui")
        ui.closeEvent = self.closeEvent
        self.show = ui.show
        self.statusBar = ui.statusBar()
        self.udp_engine = UdpCommunicationEngine()
        """初始化用户界面"""
        self.listen_port_input: QSpinBox = ui.listen_port_input
        self.listen_btn: QPushButton = ui.listen_btn
        self.listen_status: QLabel = ui.listen_status
        # 发送配置区域。
        self.target_ip_input: QLineEdit = ui.target_ip_input
        self.target_port_input: QSpinBox = ui.target_port_input
        self.json_input: QTextEdit = ui.json_input
        send_btn: QPushButton = ui.send_btn
        self.message_display: QTextEdit = ui.message_display
        clear_btn: QPushButton = ui.clear_btn
        clear_btn.clicked.connect(self.message_display.clear)
        self.statusBar.showMessage('准备就绪')
        """连接信号和槽"""
        self.listen_btn.clicked.connect(self.toggle_listening)
        send_btn.clicked.connect(self.send_message)
        # 连接UDP引擎信号。
        self.udp_engine.send_status.connect(self.on_send_status)
        self.udp_engine.receive_status.connect(self.on_receive_message)
        self.udp_engine.error_occurred.connect(self.on_error_occurred)
    
    def toggle_listening(self):
        """
        切换监听状态。
        """
        if self.udp_engine.is_listening:
            self.stop_listening()
        else:
            self.start_listening()
    
    def start_listening(self):
        """
        开始监听。
        """
        port = self.listen_port_input.value()
        if self.udp_engine.start_listening(port):
            self.listen_btn.setText('停止监听')
            self.listen_port_input.setEnabled(False)
            self.listen_status.setText('监听中...')
            self.statusBar.showMessage(f'正在监听端口 {port}')
    
    def stop_listening(self):
        """
        停止监听。
        """
        self.udp_engine.stop_listening()
        self.listen_btn.setText('开始监听')
        self.listen_port_input.setEnabled(True)
        self.listen_status.setText('已停止')
        self.statusBar.showMessage('监听已停止')
    
    def send_message(self):
        """
        发送JSON消息。
        """
        target_ip = self.target_ip_input.text().strip()
        target_port = self.target_port_input.value()
        json_text = self.json_input.toPlainText().strip()
        
        if not target_ip:
            QMessageBox.warning(None, '输入错误', '请输入目标IP地址')
            return
        if not json_text:
            QMessageBox.warning(None, '输入错误', '消息内容不能为空')
            return
        # 验证JSON格式。
        try:
            json_data = json.loads(json_text)
            formatted_json = json.dumps(json_data, ensure_ascii=False, indent=2)
        except json.JSONDecodeError as e:
            QMessageBox.warning(None, 'JSON格式错误', f'JSON格式不正确：{str(e)}')
            return
        self.udp_engine.send_json_message(target_ip, target_port, json_data)  # 发送消息。
        self.display_message('发送', f"到 {target_ip}:{target_port}\n{formatted_json}")  # 在显示区域记录发送的消息。
    
    def on_send_status(self, message, success):
        """
        处理发送状态。
        """
        if success:
            self.statusBar.showMessage(message)
        else:
            QMessageBox.critical(None, '发送错误', message)
    
    def on_receive_message(self, sender, message):
        """
        处理接收到的消息。
        """
        try:
            json_data = json.loads(message)  # 尝试美化JSON显示。
            formatted_message = json.dumps(json_data, ensure_ascii=False, indent=2)
            self.display_message('接收', f"来自 {sender}\n{formatted_message}")
        except json.JSONDecodeError:
            self.display_message('接收', f"来自 {sender}: {message}")  # 如果不是标准JSON，按原样显示。
    
    def on_error_occurred(self, error_message):
        """
        处理错误信息。
        """
        QMessageBox.critical(None, '提示', error_message)
        self.statusBar.showMessage(f'提示: {error_message}')
    
    def display_message(self, message_type, content):
        """
        在消息显示区域显示消息。
        """
        from PyQt6.QtCore import QDateTime
        current_time = QDateTime.currentDateTime()  # 使用QDateTime获取当前时间。
        timestamp = current_time.toString('HH:mm:ss.zzz')  # 格式化时间字符串。
        message = f'[{timestamp}] {message_type}: {content}\n{"-"*60}\n'
        self.message_display.insertPlainText(message)  # 添加到显示区域并自动滚动。
        scrollbar = self.message_display.verticalScrollBar()  # 自动滚动到底部。
        scrollbar.setValue(scrollbar.maximum())
    
    def closeEvent(self, event):
        """
        关闭窗口时的清理工作。
        """
        if self.udp_engine.is_listening:
            self.udp_engine.stop_listening()
        event.accept()

if __name__ == '__main__':
    from PyQt6.QtWidgets import QApplication
    import sys
    app = QApplication(sys.argv)
    window = CommunicationWindow()
    window.show()
    sys.exit(app.exec())